package IC.AST.SemanticAnalysis;

import IC.AST.*;
import IC.SymbolTables.*;
import IC.SymbolTables.Types.*;
import IC.Error.*;
import IC.Error.Error.ErrorType;


class GlobalScopeCreatorVisitor extends IC.AST.Visitor 
{
	private SymbolTable _global;
	
	private SymbolTable FatherTable(SymbolTable global, String fatherName)
	{
		if (fatherName == null)
			return global;
		else
		{
			IC.SymbolTables.Types.Symbol s = global.GetSymbol(fatherName, false);
			if (s == null)
				return null;
			
			return ((IC.SymbolTables.Types.ClassType)s.GetType()).GetSymbolTable();
		}
	}
	
	public Object Visit(Program rootNode)
	{
		_global = new SymbolTable("Global Symbol Table: " + rootNode.GetFileName(), "Global");
		rootNode.SetSymbolTable(_global);
		
		for (ClassDeclaration classDec : rootNode.GetListOfClasses())
			classDec.Visit(this);
		
		
		if(rootNode.GetLibrary() != null)
			rootNode.GetLibrary().Visit(this);
		
		return _global;
	}
	
	private SymbolTable CheckAndFixFather(SymbolTable father, ClassDeclaration classDec)
	{
		if (father != null)
			return father;
		
		String errMessage = classDec.GetSuperClassID().equals(classDec.GetID()) 
				?
						String.format("Can't extended self, in declaration of class %s.", classDec.GetID())
				:
					String.format("Can't find extended class %s form class %s.", classDec.GetSuperClassID(), classDec.GetID());
					
		ErrorStack.Push(new ErrorStringIntervaled(ErrorType.Semantic, errMessage, classDec.GetInterval()));
		return  _global;
	}
	
	public Object Visit(ClassDeclaration classDec)
	{
		classDec.SetSymbolTable(
				new SymbolTable(
						"Class Symbol Table: " + classDec.GetID(),
						classDec.GetID(),
						CheckAndFixFather(
								FatherTable(_global, classDec.GetSuperClassID()),
								classDec)));
		
		if (!_global.SetSymbol(classDec.GetID(), 
				new Symbol(Symbol.Kind.CLASS,
						new IC.SymbolTables.Types.ClassType(classDec.GetID(), classDec.GetSymbolTable()), false)))
		{
			ErrorStack.Push(
					new ErrorStringIntervaled(ErrorType.Semantic, "Found duplicate decleration of a class named " + classDec.GetID(), classDec.GetInterval()));
		}
		return null;
	}
	
	public Object Visit(Library lib)
	{
		lib.SetSymbolTable(new SymbolTable("Class Symbol Table: Library", "Library", _global));
		if (!_global.SetSymbol("Library", 
				new Symbol(Symbol.Kind.CLASS,
						new IC.SymbolTables.Types.ClassType("Library", lib.GetSymbolTable()), false)))
		{	
			ErrorStack.Push(new ErrorString(ErrorType.Semantic, "Declaration of class named Library in the main IC file. Name is reserved for libary class."));
		}
		
		return null;
	}
}